#!/usr/bin/perl
############################################################################
# Copyright (C) 2007 Lawrence Livermore National Security, LLC
# Produced at Lawrence Livermore National Laboratory.
# Written by Jim Garlick <garlick@llnl.gov>.
# UCRL-CODE-235119
# 
# This file is part of nfsroot, a network root file system utility.
# 
# nfsroot is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# nfsroot is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with nfsroot; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
############################################################################

use strict;
use Getopt::Std;
use vars qw($opt_f $opt_a $opt_d $opt_l);

sub usage
{
   print STDERR "Usage: configpxe [-f file] [-a kver | -d kver | -l]\n";
   exit 1;
}

# For an add we are going to use the 'linux' entry as a template.
# But before we do that we have to make sure it is well-formed (see issue 4)
sub validate
{
   my ($entriesp) = @_;
   my $template = $$entriesp{linux};
   my @lines = split /\n/, $template;
   my $kernel_ok = 0;
   my $append_ok = 0;
   
   foreach (@lines) {
      /^[ \t]+(.*)$/ or return 0;
      my @words = split /\s+/, $1;
      if ($words[0] eq "kernel") {
         $words[1] eq "vmlinuz" and $kernel_ok = 1;
      } elsif ($words[0] eq "append") {
         foreach (@words) {
            /^initrd=initramfs$/ and $append_ok = 1;
         }
      }
   }
   return ($kernel_ok and $append_ok);
}

# Write pxe config from $other, $template, and %entries.
#   Usage: writepxe $filename, $other, \%entries
sub writepxe
{
   my ($filename, $other, $entriesp) = @_;
   my ($label);
   my ($retval) = 0;

   if (open FILE, ">$filename") {
      print FILE "$other";
      foreach $label (keys %$entriesp) {
         print FILE "label $label\n";
         print FILE "$$entriesp{$label}";
      }
      close FILE;
      $retval = 1;
   }
   return $retval;
}

# Ingest file into $other and %entries
#   Usage: readpxe $filename, \$other, \%entries
sub readpxe
{
   my ($filename, $otherp, $entriesp) = @_;
   my $label;
   my $retval = 0;

   if (open FILE, "<$filename") {
      while (<FILE>) {
         if (/^label[ \t]+(.*)$/) { 
            $label=$1;
         } elsif (/^[ \t]+.*$/) {
            $$entriesp{$label} .= $_;
         } else {
            $$otherp .= $_;
         }
      }
      close FILE;
      $retval = 1;
   }
   return $retval;
}

# Add an entry for version by duplicating 'linux' template.
#   Usage: addentry \%entries, $version
sub addentry
{
   my ($entriesp, $version) = @_;
   my ($new);

   $new = $$entriesp{linux};
   $new =~ s/(initrd=[^\s]+)/$1-$version.img/;
   $new =~ s/(kernel\s+[^\s]+)/$1-$version/;
   if (defined($$entriesp{"linux-$version"})) {
      return 0;
   } else {
      $$entriesp{"linux-$version"} = $new;
   }
   return 1
}

# Delete an entry for version.
#   Usage: delentry \%entries, $version
sub delentry
{
   my ($entriesp, $version) = @_;

   if (exists $$entriesp{"linux-$version"}) {
      delete $$entriesp{"linux-$version"};
      return 1;
   }
   return 0;
}

# List entries (skip 'linux' entry)
#   Usage: listentries \%entries
sub listentries
{
   my ($entriesp) = @_;
   my ($label);

   foreach $label (keys %$entriesp) {
      if ($label ne "linux") {
          print "$label\n";
      }
   }
}

my ($label, %entries, $other);
my $path_config = "/boot/pxelinux.cfg";

getopts('lf:a:d:') or usage;
if ($opt_f) {
   $path_config = $opt_f;
}

if (!readpxe $path_config, \$other, \%entries) {
   die "$path_config: $!";
}
if (!validate (\%entries)) {
   die "$path_config: linux stanza is invalid, see configpxe(8)";
}
if ($opt_a) {
   if (!addentry \%entries, $opt_a) {
      die "entry for $opt_a exists";
   }
   if (!writepxe $path_config, $other, \%entries) {
      die "$path_config: $!";
   }
} elsif ($opt_d) {
   if (!delentry \%entries, $opt_d) {
      die "entry for $opt_d not found";
   }
   if (!writepxe $path_config, $other, \%entries) {
      die "$path_config: $!";
   }
} elsif ($opt_l) {
   listentries \%entries;
}

exit (0);

# vi:tabstop=3 shiftwidth=3 expandtab
